/*
 * tinwi.cpp
 *
 *  Created on: 13 июн. 2024 г.
 *      Author: layst
 */

#include "tinwi.h"
#include "kl_libG070.h"
#include "LEDs.h"
#include "BaseSequencer.h"

static const int32_t ktinwi_cnt_max = 36;
static const int32_t kframe_period = 7;

Params param_set[] = {
        {
                270, // smooth_min
                540, // smooth_max
                45,  // brt_min
                108, // brt_max
                18,  // tinwi_cnt
                4    // off_brt
        },
//        {
//                270, // smooth_min
//                540, // smooth_max
//                180,  // brt_min
//                255, // brt_max
//                7,  // tinwi_cnt
//                3    // off_brt
//        },
};
static const uint32_t kparam_set_cnt = countof(param_set);

Params *curr_params = &param_set[0];

// LEDs frame buffer
uint16_t frame_buf[LED_CNT_TOTAL];

class Tinwe : public BaseSequencer_t<LedSmoothChunk_t> {
private:
    int32_t curr_brt = 0;
    LedSmoothChunk_t lsq[3] = { {Chunk::Setup}, {Chunk::Setup}, {Chunk::End} };
public:
    int32_t led_indx = -1;
    void GenerateAndStart();
    void SetBrightness(uint16_t abrt) { frame_buf[led_indx] = abrt; }
    // Base Sequencer's
    void SwitchOff() { frame_buf[led_indx] = 0; curr_brt = 0; }
    bool AdjustAndCheckIfGotoNextChunk() {
        int32_t target_brt = pcurrent_chunk->brightness; // To make things shorter
        if(curr_brt == target_brt) return true;
        // Not equal
        if(pcurrent_chunk->smooth_value == 0) { // If smooth is zero,
            SetBrightness(target_brt); // set brt now and goto next chunk
            curr_brt = target_brt;
            return true;
        }
        else { // smooth_value != 0, adjust it
            if     (curr_brt < target_brt) curr_brt++;
            else if(curr_brt > target_brt) curr_brt--;
            SetBrightness(curr_brt);
            // Check if equal now
            if(curr_brt == target_brt) return true;
            // Not equal, calculate time to next adjustment
            int32_t delay = ClrCalcDelay(curr_brt, pcurrent_chunk->smooth_value);
            SetupDelay(delay);
            return false;
        } // smooth_value != 0
    }
};

static Tinwe tinwi[ktinwi_cnt_max];

static inline bool TinweIndxIsOccupied(int32_t indx) {
    for(Tinwe &tinwe : tinwi)
        if(tinwe.led_indx == indx) return true;
    return false;
}

void Tinwe::GenerateAndStart() {
    // Get new led indx
    while(true) {
        int32_t new_indx = Random::Generate(0, (LED_CNT_TOTAL-1));
        if(!TinweIndxIsOccupied(new_indx)) {
            led_indx = new_indx;
            break;
        }
    }
//    led_indx = 36; // DEBUG
    // Generate new params
    lsq[0].smooth_value = curr_params->GetSmooth();
    lsq[0].brightness   = curr_params->GetBrt();
    lsq[1].smooth_value = curr_params->GetSmooth();
    lsq[1].brightness   = curr_params->off_brt;
    // Start it
    StartOrRestart(lsq);
}

static THD_WORKING_AREA(waEffThread, 256);
static void EffThread(void *arg) {
    chRegSetThreadName("Tinwi");
    uint32_t time_passed = 0, params_indx = 0;;
    while(true) {
        chThdSleepMilliseconds(kframe_period);
        Leds::ShowPic(frame_buf);
        // Check if new generation required
        for(int32_t i=0; i<curr_params->tinwi_cnt; i++)
            if(tinwi[i].IsIdle())
                tinwi[i].GenerateAndStart();
        // Check if change params
        time_passed += kframe_period;
        if(time_passed > 9000) {
            time_passed = 0;
            params_indx++;
            if(params_indx >= kparam_set_cnt) params_indx = 0;
            curr_params = &param_set[params_indx];
        }
    } // while true
}

void InitTinwi() {
    chThdCreateStatic(waEffThread, sizeof(waEffThread), HIGHPRIO, (tfunc_t)EffThread, nullptr);
}
